#include "main.h"


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_brace
 *  Description:  parse brace ([, ]) in the tree, parse the first element in the list,
 *  then remove it and add the new element to the end of the list
 *  Return: 0 parsed 1 meet the end
 * =====================================================================================
 */
gint parse_brace (TreeAttr *attr)
{
  gint m;
  GString *expr = NULL;

  // search in the list and find a element to parse
  if (!attr->head) {
    if(is_brace_clean (attr->expr))
      expr = attr->expr;
  } else {
    gint i;
    for (i = 0; i < g_slist_length (attr->head); i++) {
      GSList *el = g_slist_nth (attr->head, i);
      if (is_brace_clean(el->data)) {
        attr->head = g_slist_remove_link (attr->head, el);
        expr = el->data;
        break;
      }
    }
  }

  // iteration exit condition, the expression tree clean
  if (!expr) {
    g_debug ("[parse_brace]expr %d: clean up\n", attr->index);
    return 1;
  }

  g_debug ("[parse_brace]parser : (%s)", expr->str);

  // find matched brace, start from 0, this means find the most outer level of the brace
  gint start_pos, end_pos;
  parse_brace_get_left (expr, 0, &start_pos);
  parse_brace_get_right (expr, start_pos, &end_pos);

  g_debug ("[parse_brace]start=%d, end=%d", start_pos, end_pos);

  // spawn expression which remove brace token
  GString *spawn1 = g_string_new (NULL);
  for (m = 0; m < expr->len; m++) {
    if (m != start_pos && m != end_pos) {
      g_string_append_c (spawn1, expr->str[m]);
    }
  }
  g_debug ("[parse_brace]spawn1 : (%s)", spawn1->str);

  // spawn expression without chars inside brace
  GString *spawn2 = g_string_new (NULL);
  for (m = 0; m < expr->len; m++) {
    if (m < start_pos) {
      g_string_append_c (spawn2, expr->str[m]);
    } else if (m > end_pos) {
      g_string_append_c (spawn2, expr->str[m]);
    }
  }
  g_debug ("[parse_brace]spawn2 : (%s)\n", spawn2->str);

  // remove old and add new
/*   if (attr->head) {
 *     GSList *old = g_slist_nth (attr->head, 0);
 *     attr->head = g_slist_delete_link (attr->head, old);
 *   }
 */

  // add like this sequence make algorithm come true
  attr->head = g_slist_append (attr->head, spawn1);
  attr->head = g_slist_append (attr->head, spawn2);

  return 0;
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_brace_get_left
 *  Description:  get the most left brace position
 *  Return: pos: position of the TOKEN_BRACE_START
 * =====================================================================================
 */
void parse_brace_get_left (GString *expr, gint start, gint *pos)
{
  gint i = start;
  while (i < expr->len) {
    if (expr->str[i] == TOKEN_BRACE_START) {
      *pos = i;
      return;
    }
    i++;
  }

  g_return_if_reached();
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_brace_get_right
 *  Description:  get matched brace position
 *  Return: pos: position of the TOKEN_BRACE_END
 * =====================================================================================
 */
void parse_brace_get_right (GString *expr, gint start, gint *pos)
{
  GQueue *stack = g_queue_new ();

  // from start position, search until non-matched brace
  gint i = start;
  while (i < expr->len) {
    if (expr->str[i] == TOKEN_BRACE_START) {
      g_queue_push_tail (stack, "TOKEN_BRACE_START");
    } else if (expr->str[i] == TOKEN_BRACE_END) {
      g_queue_pop_tail (stack);
    }

    if (g_queue_is_empty (stack)) {
      *pos = i;
      return;
    }
    i++;
  }

  g_return_if_reached();
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_mutex
 *  Description:  parse mutex ( | ) in the tree, parse the first element in the list,
 *  remove the parsed one, and then add the two new element to the end of the list.
 *  Return: 0 parsed, 1 meet the end
 * =====================================================================================
 */
gint parse_mutex (TreeAttr *attr)
{
  GString *expr = NULL;

  if (!attr->head) {
    if (is_mutex_clean(attr->expr))
      expr = attr->expr;
  } else {
    // test along the list whether parse clear
    gint i;
    for (i = 0; i < g_slist_length (attr->head); i++) {
      GSList *el = g_slist_nth (attr->head, i);
      // if current expression parsed clean, check next
      if (is_mutex_clean(el->data)) {
        attr->head = g_slist_remove_link (attr->head, el);
        expr = el->data;
        break;
      }
    }
  }

  // iteration exit condition
  if (!expr) {
    g_debug ("[parse_mutex]expr %d: clean up\n", attr->index);
    return 1;
  }

  g_debug ("[parse_mutex]parser : (%s) len=%d",expr->str, expr->len);


  // calculate three position of the expression tree
  gint i, start_pos = 0, m_pos, end_pos = expr->len - 1;

  // find mutex, left = 0(spawn1), right = 1(spawn2)
  // left first as default
  gint first = 0;

  for (i = 0; i < expr->len; i++) {
    if (expr->str[i] == TOKEN_MUTEX) {
      m_pos = i;
      break;
    }
  }

  parse_mutex_get_left (expr, m_pos, &start_pos, &first);
  parse_mutex_get_right (expr, m_pos, &end_pos, &first);

  g_debug ("[parse_mutex]start=%d, m=%d, end=%d, first=%d", start_pos, m_pos, end_pos, first);

  // spawn the expression, left
  GString *spawn1 = g_string_new (NULL);
  for (i = 0; i < expr->len; i++) {
    if (i < m_pos) {
      g_string_append_c (spawn1, expr->str[i]);
    } else if (i >= end_pos) {
      g_string_append_c (spawn1, expr->str[i]);
    }
  }

  g_debug ("[parse_mutex]spawn1 : (%s)",spawn1->str);


  // right
  GString *spawn2 = g_string_new (NULL);
  for (i = 0; i < expr->len; i++) {
    if (i > m_pos) {
      g_string_append_c (spawn2, expr->str[i]);
    } else if (i <= start_pos) {
      g_string_append_c (spawn2, expr->str[i]);
    }
  }

  g_debug ("[parse_mutex]spawn2 : (%s)\n",spawn2->str);

  // remove old and add newly spawned
/*   if (attr->head) {
 *     GSList *old = g_slist_nth (attr->head, 0);
 *     attr->head = g_slist_delete_link (attr->head, old);
 *   }
 */

  // add like this sequence make algorithm come true
  if (first) {
    attr->head = g_slist_append (attr->head, spawn2);
    attr->head = g_slist_append (attr->head, spawn1);
  } else {
    attr->head = g_slist_append (attr->head, spawn1);
    attr->head = g_slist_append (attr->head, spawn2);
  }

  return 0;
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_mutex_get_left
 *  Description:  get the left position of the mutex token in the expression
 *  Return: m_pos: left position, first: still left mutex token to parse or not
 * =====================================================================================
 */
void parse_mutex_get_left (GString *expr, gint m_pos, gint *pos, gint *first)
{
  gint i;
  GQueue *stack = g_queue_new ();

  for (i = m_pos; i >= 0; i--) {
    if (is_token_end(expr->str[i])) {
      g_queue_push_tail (stack, expr->str + i);

    } else if (is_token_start(expr->str[i])) {
      if (g_queue_is_empty (stack)) {
        *pos = i;
        return;
      } else {
        g_queue_pop_tail (stack);
      }

    } else if (expr->str[i] == TOKEN_MUTEX) {
      *first = 0;
    }

  }

  g_return_if_reached ();
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_mutex_get_right
 *  Description:  get the right position of the mutex token in the expression
 *  Return: m_pos: right position, first: still left mutex token to parse or not
 * =====================================================================================
 */
void parse_mutex_get_right (GString *expr, gint m_pos, gint *pos, gint *first)
{
  gint i;
  GQueue *stack = g_queue_new ();

  for (i = m_pos; i <= expr->len-1; i++) {
    if (is_token_start(expr->str[i])) {
      g_queue_push_tail (stack, expr->str + i);

    } else if (is_token_end(expr->str[i])) {
      if (g_queue_is_empty (stack)) {
        *pos = i;
        return;
      } else {
        g_queue_pop_tail (stack);
      }

    } else if (expr->str[i] == TOKEN_MUTEX) {
      *first = 1;
    }
  }

  g_return_if_reached ();
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_curly
 *  Description:  parse curly ({, }) in the tree, parse the curly in the list,
 *  remove false expression.
 *  Return: 0 parsed, 1 meet the end
 * =====================================================================================
 */
gint parse_curly (TreeAttr *attr)
{
  GString *expr = NULL;

  if (!attr->head) {
    if (is_curly_clean(attr->expr))
      expr = attr->expr;
  } else {
    // test along the list whether parse clear
    gint i;
    for (i = 0; i < g_slist_length (attr->head); i++) {
      GSList *el = g_slist_nth (attr->head, i);
      // if current expression parsed clean, check next
      if (is_curly_clean(el->data)) {
        attr->head = g_slist_remove_link (attr->head, el);
        expr = el->data;
        break;
      }
    }
  }

  // iteration exit condition
  if (!expr) {
    g_debug ("[parse_curly]expr %d: clean up\n", attr->index);
    return 1;
  }

  g_debug ("[parse_curly]parser : (%s) len=%d",expr->str, expr->len);

  gint start_pos, end_pos;
  parse_curly_get_left (expr, 0, &start_pos);
  parse_curly_get_right (expr, start_pos, &end_pos);

  g_debug ("[parse_curly]start=%d, end=%d\n", start_pos, end_pos);

  // check expression to decide how to parse
  gint i, exist = 0;
  for (i = start_pos + 1; i < end_pos; i++) {
    if (expr->str[i] != ' ') {
      // this expression should exist.
      exist = 1;
    }
  }

  // spawn expression
  GString *spawn1 = g_string_new (NULL);

  if (exist) {

    for (i = 0; i < expr->len; i++) {
      if (i != start_pos && i != end_pos) {
        g_string_append_c (spawn1, expr->str[i]);
      }
    }

    // add the expression to the queue
    attr->head = g_slist_append (attr->head, spawn1);

    g_debug ("[parse_curly]spawn1 : (%s)\n", spawn1->str);
  }

  // remove the old expression
/*   if (attr->head) {
 *     GSList *old = g_slist_nth (attr->head, 0);
 *     attr->head = g_slist_delete_link (attr->head, old);
 *   }
 */

  return 0;
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_curly_get_left
 *  Description:  get the left position of the curly
 *  Return: position of the TOKEN_CURLY_START
 * =====================================================================================
 */
void parse_curly_get_left (GString *expr, gint start, gint *pos)
{
  gint i = start;

  while (i < expr->len) {
    if (expr->str[i] == TOKEN_CURLY_START) {
      *pos = i;
      return;
    }

    i++;
  }

  g_return_if_reached ();
}



/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_curly_get_right
 *  Description:  get the matched right position of the curly
 *  Return: position of the TOKEN_CURLY_END
 * =====================================================================================
 */
void parse_curly_get_right (GString *expr, gint start, gint *pos)
{
  gint i = start;
  GQueue *stack = g_queue_new ();

  while (i < expr->len) {
    if (expr->str[i] == TOKEN_CURLY_START) {
      g_queue_push_tail (stack, "TOKEN_CURLY_START");
    } else if (expr->str[i] == TOKEN_CURLY_END) {
      g_queue_pop_tail (stack);
    }

    if (g_queue_is_empty (stack)) {
      *pos = i;
      return;
    }

    i++;
  }

  g_return_if_reached ();
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  parse_angle
 *  Description:  parse angle (<, >) in the tree
 * =====================================================================================
 */
gint parse_angle (TreeAttr *attr)
{
  gint i;
  // if not contain any angle char, return
  if (!is_angle_clean (attr->expr))
    return;

  // expression string process
  GString *expr = g_string_new (NULL);
  for (i = 0; i < attr->expr->len; i++) {
    if (attr->expr->str[i] != TOKEN_ANGLE_START && \
        attr->expr->str[i] != TOKEN_ANGLE_END && \
        attr->expr->str[i] != ' ' && \
        attr->expr->str[i] != '-') {
      g_string_append_c (expr, attr->expr->str[i]);
    }
  }

  g_debug ("[parse_angle]: str = %s", expr->str);

  // combination
  GString *str = parse_angle_get_comb_str (expr);
  GSList *head = parse_angle_comb_to_list (str);
  g_string_free (str, TRUE);
  g_string_free (expr, TRUE);

  // link
  attr->head = head;

  // tree process : add '-' at the beginning
  GSList *list = attr->head;
  while (list) {
    GSList *next = list->next;
    GString *cur_str = list->data;

    g_string_prepend_c (cur_str, '-');

    list = next;
  }

  return 0;
}
